// =============================================
// FD Analog Signal Noise
// Made by Ubik and Claude 2026
// =============================================
// Adds animated analog noise every frame.
// Luminance noise and separate color channel
// noise with controllable grain size.
// Connect video source to video in 1.
// =============================================

// ISADORA_PLUGIN_DESC("Analog Signal Noise - adds live animated analog noise every frame. Luminance noise and separate RGB color channel noise with controllable grain size.")

// ISADORA_FLOAT_PARAM(noise_amount, nzam, 0.0, 1.0, 0.12, "Overall noise strength - how visible the grain is.")
// ISADORA_FLOAT_PARAM(color_noise, cnz, 0.0, 1.0, 0.4, "0 = pure luminance noise (B&W grain), 1 = full color channel noise (RGB grain separately).")
// ISADORA_FLOAT_PARAM(grain_size, grsz, 1.0, 8.0, 1.0, "Grain size. 1 = pixel grain, higher = coarser sand grain.")

uniform float noise_amount;
uniform float color_noise;
uniform float grain_size;
uniform float iTime;
uniform vec3 iResolution;
uniform sampler2D tex0;

float rand(vec2 co)
{
    return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
}

void main()
{
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    vec4 original = texture2D(tex0, uv);

    // Quantize to grain size - coarser grain at higher values
    vec2 grainUV = floor(gl_FragCoord.xy / grain_size) / (iResolution.xy / grain_size);

    float t = iTime;
    float noiseR = rand(grainUV + vec2(t * 0.3171, t * 0.1321)) - 0.5;
    float noiseG = rand(grainUV + vec2(t * 0.2713 + 0.5, t * 0.3719 + 0.3)) - 0.5;
    float noiseB = rand(grainUV + vec2(t * 0.1933 + 0.9, t * 0.4127 + 0.7)) - 0.5;

    // Luminance noise: same value on all channels (B&W grain)
    float lumaNoiseVal = rand(grainUV + vec2(t * 0.2531, t * 0.3847)) - 0.5;
    vec3 lumaNoise = vec3(lumaNoiseVal);

    // Color noise: separate values per channel (colored grain)
    vec3 colorNoiseVec = vec3(noiseR, noiseG, noiseB);

    vec3 finalNoise = mix(lumaNoise, colorNoiseVec, color_noise);
    vec3 result = clamp(original.rgb + finalNoise * noise_amount, 0.0, 1.0);
    gl_FragColor = vec4(result, original.a);
}
